package martin.tpfinal.grabar.grafico;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.TargetDataLine;
import javax.swing.Timer;

import martin.tpfinal.grabar.motor.ColaSalida;
import martin.tpfinal.grabar.principal.Principal;

/** 
 * Reads data from the input channel and writes to the output stream
 */
class Capturar implements Runnable {

    private TargetDataLine line;
    private Thread thread;
	private VentanaPrincipal ventana;

    public Capturar(VentanaPrincipal ventana){
    	this.ventana=ventana;
    }
    
    
    public void start() {
        ventana.setErrStr(null);
        thread = new Thread(this);
        thread.setName("Capture");
        thread.start();
    }

    public void stop() {
        thread = null;
    }
    
    public void shutDown(String message) {
    	ventana.setErrStr(message);
    	if (ventana.getErrStr() != null && thread != null) {
            thread = null;
            ventana.getSamplingGraph().stop();
            ventana.getLoadB().setEnabled(true);
            ventana.getPlayB().setEnabled(true);
            ventana.getPausB().setEnabled(false);
            ventana.getSaveWaveB().setEnabled(true);
            ventana.getCaptB().setText("Record");
            System.err.println(ventana.getErrStr());
            ventana.getSamplingGraph().repaint();
        }
    }

    public void run() {

    	ventana.setDuration(0);
        numProceso=0;
        ventana.setAudioInputStream(null);
                
        new TestData(this, ventana);

/*        
        // define the required attributes for our line, 
        // and make sure a compatible line is supported.

        AudioFormat format = ventana.getFormatControls().getFormat();
 
        
        DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
              
        System.out.println(format.toString());
        System.out.println(info.toString());
        
        if (!AudioSystem.isLineSupported(info)) {
            shutDown("Line matching " + info + " not supported.");
            return;
        }

        // get and open the target data line for capture.

        try {
            line = (TargetDataLine) AudioSystem.getLine(info);
            line.open(format, line.getBufferSize());
        } catch (LineUnavailableException ex) { 
            shutDown("Unable to open the line: " + ex);
            return;
        } catch (SecurityException ex) { 
            shutDown(ex.toString());
            VentanaPrincipal.showInfoDialog();
            return;
        } catch (Exception ex) { 
            shutDown(ex.toString());
            return;
        }

        // play back the captured audio data
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int frameSizeInBytes = format.getFrameSize();
        int bufferLengthInFrames = line.getBufferSize() / 8;
        int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;
        byte[] data = new byte[bufferLengthInBytes];
        int numBytesRead;
        
        int numBytesCadaDiezMili = (int)((format.getFrameRate()*format.getFrameSize())/100);
        byte[] data2 = new byte[numBytesCadaDiezMili];
        
        Timer timer=createTmmer();
        line.start();
        	           	            
        timer.start();
        
        int[] silence=new int[20];
        int[] energy=new int[20];
        int indice=0;
        double cero=0;
        double energia=0;
        int counter=0;
        boolean esperando=true;
        
        //voy a suponer que mis palabras deben durar 0.55 segundos, serian 550 milisegundos
        //entonces cuando detecto que es una palabra, debo grabar 55 veces del read
        while (thread != null) {
//            if((numBytesRead = line.read(data, 0, bufferLengthInBytes)) == -1) {
            if((numBytesRead = line.read(data2, 0, numBytesCadaDiezMili)) == -1) {
                break;
            }
            
//            if (indice >50 && indice<71){//para estabilizarlo
//                if (indice<70){
//                	silence[(indice-50)] = getZeroCrosingRateWithoutCero(numBytesRead, data2);
//                	energy[(indice-50)] = getEnergy(numBytesRead, data2);
//                	indice++;
//				}else{
//					cero=getZeroCrosingRatePaper(silence);
//					energia=getEnergyPaper(energy);
//					System.out.println("CERO----------------"+cero);
//					System.out.println("ENERGIA-------------"+energia);
//					indice++;
//				}
//            }else {indice++;}    
//
//
//            if (indice>71){
//                processData(numBytesRead,data2);
//                int e=getEnergy(numBytesRead, data2);
//                //getZeroCrosingRate(numBytesRead, data2);
//                int ZCR=getZeroCrosingRateWithoutCero(numBytesRead, data2);
//                if (e > energia && ZCR > cero )
//                	out.write(data2, 0, numBytesRead);
//            }
//            enviarpalabra();

            
            
             //* Grabando en calidad estudio
             //* Volumen mic 75
             //* potencia +20
             
            
            processData(numBytesRead,data2);
            
            if (esperando){
            	System.out.println("Esta esperando***************");
                int e=getEnergy(numBytesRead, data2);
                int ZCR=getZeroCrosingRateWithoutCero(numBytesRead, data2);
// original     if (e > 5000 && ZCR > 80 ){
                if (e > 15000 && ZCR > 29 ){
                	out = new ByteArrayOutputStream();
                	out.write(data2, 0, numBytesRead);
                	counter++;
                	esperando=false;
                }
            }
            else{
                out.write(data2, 0, numBytesRead);
                counter++;
            }
            
            if (counter == 65){
            	procesarYenviarpalabra(out,format,frameSizeInBytes);
            	counter=0;
            	esperando=true;
        	}
        }
        timer.stop();

        // we reached the end of the stream.  stop and close the line.
        line.stop();
        line.close();
        line = null;

        System.out.println(("Totales pot "+mayorPot));
        System.out.println(("Totales cero "+mayorCero));
        
        // stop and close the output stream
        try {
            out.flush();
            out.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }

        // load bytes into the audio input stream for playback

        byte audioBytes[] = out.toByteArray();
        ByteArrayInputStream bais = new ByteArrayInputStream(audioBytes);
        ventana.setAudioInputStream( new AudioInputStream(bais, format, audioBytes.length / frameSizeInBytes));

        long milliseconds = (long)((ventana.getAudioInputStream().getFrameLength() * 1000) / format.getFrameRate());
        ventana.setDuration( milliseconds / 1000.0);

        try {
        	ventana.getAudioInputStream().reset();
        } catch (Exception ex) { 
            ex.printStackTrace(); 
            return;
        }

        ventana.getSamplingGraph().createWaveForm(audioBytes);
*/  
	}

    private void procesarYenviarpalabra(ByteArrayOutputStream out,AudioFormat format,int frameSizeInBytes) {
        // stop and close the output stream
        try {
            out.flush();
            out.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }

        // load bytes into the audio input stream for playback
        byte audioBytes[] = out.toByteArray();
        ByteArrayInputStream bais = new ByteArrayInputStream(audioBytes);
        AudioInputStream audioFile=new AudioInputStream(bais, format, audioBytes.length / frameSizeInBytes);
        ventana.setAudioInputStream( audioFile);

        long milliseconds = (long)((ventana.getAudioInputStream().getFrameLength() * 1000) / format.getFrameRate());
        ventana.setDuration( milliseconds / 1000.0);

        try {
        	ventana.getAudioInputStream().reset();
        } catch (Exception ex) { 
            ex.printStackTrace(); 
            return;
        }

        ventana.getSamplingGraph().createWaveForm(audioBytes);
        
        String fileName="trabar"+index++ +".wav";
        ventana.saveToFile(fileName, AudioFileFormat.Type.WAVE);

 //       enviarpalabra(audioBytes);
	}
int index=0;
	
	int numProceso=0;
	private void processData(int numBytesRead, byte[] data) {
        int max=0;
        int min=0;
        int actual=0;
        System.out.println("-----------------------"+ numProceso++ +"-----------------------------");
        System.out.println("leyo: "+numBytesRead);
		for (int i = 0; i < numBytesRead; i++) {
			actual=data[i];
			System.out.print(actual+" ; ");
			if (max<actual)
				max=actual;

			if (min>actual)
				min=actual;

		}
		System.out.println();
		System.out.println("El maximo es : "+max+"; El minimo es :"+min);
		int e = getEnergy(numBytesRead, data);
		if (e>15000){ System.out.println("********************* MAYOR A 15000"); mayorPot++;}
		Integer CERO = getZeroCrosingRateWithoutCero(numBytesRead, data);
		if (CERO>29){ System.out.println("********************** MAYOR A 29"); mayorCero++;}

	}

	static int mayorPot=0;
	static int mayorCero=0;
    
	private int getEnergy(int numBytesRead, byte[] data) {
        int sum=0;
        for (int i = 0; i < numBytesRead; i++) {
			sum+=Math.abs(data[i]);
		}
		System.out.println("La enegia es: "+sum);
		return sum;
	}

	private int getZeroCrosingRate(int numBytesRead, byte[] data) {
        int sum=0;

        for (int i = 1; i < numBytesRead; i++) {
			
        	int prevValue=data[i-1];
        	
			if (data[i]>0 && (prevValue==0 || prevValue<0)) 
        	   	sum++;
			if (data[i]<0 && (prevValue==0 || prevValue>0)) 
        	   	sum++;

        }
		System.out.println("Cruza el 0 : "+sum);
		return sum;
	}

	private int getZeroCrosingRateWithoutCero(int numBytesRead, byte[] data) {
        int sum=0;

        int prevValue=data[0]; 
        
        for (int i = 1; i < numBytesRead-1; i++) {
			
        	if (data[i]!=0){
            	if (data[i]>0 && prevValue<0 && data[i+1]>0) 
            	   	sum++;
				if (data[i]<0 && prevValue>0 && data[i+1]<0) 
            	   	sum++;
				prevValue=data[i];
        	}
        }
		System.out.println("Cruza el 0 : "+sum);
		return sum;
	}

	private int getZeroCrosingRateWithoutCeroOld(int numBytesRead, byte[] data) {
        int sum=0;

        int prevValue=data[0]; 
        
        for (int i = 1; i < numBytesRead; i++) {
			
        	if (data[i]!=0){
            	if (data[i]>0 && prevValue<0) 
            	   	sum++;
				if (data[i]<0 && prevValue>0) 
            	   	sum++;
				prevValue=data[i];
        	}
        }
		System.out.println("Cruza el 0 : "+sum);
		return sum;
	}

	/*
	 * Se calcula sobre 100 mseg, en nuestro caso 10 periodos, ya que 1 periodo -> 10 mseg
	 * 
	 */
	private double getEnergyPaper(int[] data) {
        //valor umbral de energia

		//el pico de energia maximo del silencio
		double IMX=0;
		//la energia promedio del silencio
		double IMN=0;
			
		//el limite inferior del umbral
		double ITL=0;
		//el limite superior del umbral
		double ITU=0;
		
		IMX=data[0];
		for (int i = 1; i < data.length; i++) {
			if (data[i]>IMX)
				IMX=data[i];
		}
		IMN=media(data);
		double desv=desviacionEstandar(data);
		System.out.println("Maxima: "+IMX+", media: "+IMN+", desviacion: "+desv);
		
		double I1=0.03*(IMX-IMN)+IMN;
		double I2=4*IMN;
		ITL=Math.min(I1,I2);
		ITU=5*ITL;
		System.out.println("ENER      ITL : "+ITL+", ITU: "+ITU);
		
		//probar con 1.5*IMX o la media mas 2 veces la desviacion (estuve usando 10000)
		double enegia=Math.max(1.5*IMX,IMN+2*desv);//,2000);
		
		System.out.println("ENER            1.5*IMX: "+1.5*IMX+ "|||| IMN+2*desv: "+(IMN+2*desv));
		return enegia;	
		
	}
	
	
	/*
	 * Se calcula sobre 100 mseg, en nuestro caso 10 periodos, ya que 1 periodo -> 10 mseg
	 * 
	 */
	private double getZeroCrosingRatePaper(int[] data) {
        //valor umbral de cruces de cero
		//es el minimo entre un valor umbral fijo IF y (la suma del valor medio + la desviacion estandar)
		// IZCT = MIN ( IF , IZC + 2G(IZC) )
		double IZCT=0;
			
		//umbral fijo	//125
		double IF=	80;
		
		//media de la taza de cruces de cero durante el silencio 
		//(sumo todos los calculos que hice en los distintos periodos y los dividos por la cantidad que son, es un promedio comun)
		double media=media(data);
		double desvStandar=desviacionEstandar(data);
		System.out.println("CERO Media: "+media+", desviacion: "+desvStandar);
		double IZC_final = media(data)+(2*desviacionEstandar(data));
		System.out.println("CERO IZC : "+IZC_final);
		IZCT=Math.max(IF, IZC_final);
		//
		return IZCT;	
		
	}
	
	
	public double media(int val[]){
		int suma=0;
		for (int i = 0; i < val.length; i++) {
			suma+= val[i];
		}
		
		return suma/val.length;
	}
	
	public double desviacionEstandar(int arreglo[])  {
		double suma = 0;
		double media = media(arreglo);
		double desviacion = 0;
		double diferencia = 0;
		
		for(int i = 0; i < arreglo.length; i++) {
			diferencia = (arreglo[i] - media);
			desviacion += Math.pow(diferencia, 2);
		}
		desviacion = desviacion/arreglo.length;
		return Math.sqrt(desviacion);
		
	}

	
    int tiempo=1;
	private Timer createTmmer() {
		tiempo=0;
		Timer timer = new Timer (1000, new ActionListener ()
	    {
	        public void actionPerformed(ActionEvent e)
	        {
	        	System.out.println();
	            System.out.println("***********Paso el segundo: "+tiempo++);
	            System.out.println();
	         }
	    });
	    			    
		return timer;
	}


	public TargetDataLine getLine() {
		return line;
	}


	public void setLine(TargetDataLine line) {
		this.line = line;
	}


	public Thread getThread() {
		return thread;
	}


	public void setThread(Thread thread) {
		this.thread = thread;
	}

	/**
     * Cuando reconozco que no es silencio, entonces grabo cierto tiempo, y llamo a esta funcion
     * esta se encargara de enviar la informacion por socket al reconocedor de palabras
     * de esta forma el sistema sigue grabando sonido, y puedo reconocer continuamente.
     */
    private void enviarpalabra(byte[] audioFile) {
    	ColaSalida cola=ColaSalida.getInstance();
    	cola.addElement(audioFile);
		
	}

	
	
	

} // End class Capture
